package com.jingdianjichi.redis.util;

import com.jingdianjichi.redis.exception.ShareLockException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
public class RedisShareLockUtil {

    @Resource
    private RedisUtil redisUtil;

    // 锁超时时间，默认为 1000 毫秒
    private Long TIME_OUT = 1000L;

    /**
     * 加锁
     *
     * @param lockKey   锁的键
     * @param requestId 请求标识
     * @param time      锁的过期时间
     * @return 是否成功加锁
     */
    public boolean lock(String lockKey, String requestId, Long time) {
        // 参数校验
        if (StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time < 0) {
            throw new ShareLockException("分布式锁加锁参数异常");
        }
        long currentTime = System.currentTimeMillis();
        long outTime = currentTime + TIME_OUT;
        Boolean result = false;
        // 加锁可以自旋
        while (currentTime < outTime) {
            // 使用 setnx 命令来设置锁
            result = redisUtil.setNx(lockKey, requestId, time, TimeUnit.MILLISECONDS);
            if (result) {
                // 加锁成功
                return result;
            }
            try {
                Thread.sleep(100); // 自旋等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            currentTime = System.currentTimeMillis();
        }
        return result;
    }

    /**
     * 解锁
     *
     * @param key       锁的键
     * @param requestId 请求标识
     * @return 是否成功解锁
     */
    public boolean unlock(String key, String requestId) {
        if (StringUtils.isBlank(key) || StringUtils.isBlank(requestId)) {
            throw new ShareLockException("分布式锁解锁参数异常");
        }
        try {
            String value = redisUtil.get(key);
            if (requestId.equals(value)) {
                boolean del = redisUtil.del(key);
                return true;
            }
        } catch (Exception e) {
            // 异常处理，记录日志等
        }
        return false;
    }

    /**
     * 尝试加锁
     *
     * @param lockKey   锁的键
     * @param requestId 请求标识
     * @param time      锁的过期时间
     * @return 是否成功加锁
     */
    public boolean tryLock(String lockKey, String requestId, Long time) {
        if (StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time <= 0) {
            throw new ShareLockException("分布式锁-尝试加锁参数异常");
        }
        // 使用 setnx 命令来尝试加锁
        return redisUtil.setNx(lockKey, requestId, time, TimeUnit.MILLISECONDS);
    }
}
